package localize

import (
	"embed"
	"encoding/json"
	"github.com/BurntSushi/toml"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/nicksnyder/go-i18n/v2/i18n"
	"golang.org/x/text/language"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
	"sync"
	"text/template"
)

type (
	I18nBundle struct {
		bundle *i18n.Bundle
		reMux  sync.RWMutex
		funks  template.FuncMap
	}

	Localized struct {
		lang     string
		bundle   *I18nBundle
		localize *i18n.Localizer
		funks    template.FuncMap
	}
)

var (
	defaultI18nBundle = &I18nBundle{
		bundle: i18n.NewBundle(language.English),
		funks:  template.FuncMap{},
	}
)

// NewBundleFromEmbedFs 从嵌入式文件中加载语言文件
func NewBundleFromEmbedFs(fs embed.FS, pattern string) (*I18nBundle, error) {
	bundle := i18n.NewBundle(language.English)
	setDefaultUnmarshalFunc(bundle)

	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}

	for _, m := range matches {
		_, err := bundle.LoadMessageFileFS(fs, m)
		if err != nil {
			return nil, err
		}
	}

	return &I18nBundle{
		bundle: bundle,
		funks:  template.FuncMap{},
	}, nil
}

// NewBundleFromFile 从文件中加载语言文件 --根据匹配项 ./*.toml
func NewBundleFromFile(pattern string) (*I18nBundle, error) {
	bundle := i18n.NewBundle(language.English)
	setDefaultUnmarshalFunc(bundle)

	matches, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}

	for _, m := range matches {
		_, err := bundle.LoadMessageFile(m)
		if err != nil {
			return nil, err
		}
	}

	return &I18nBundle{
		bundle: bundle,
		funks:  template.FuncMap{},
	}, nil
}

// NewBundleFromPath 从指定路径加载语言文件
func NewBundleFromPath(path string) (*I18nBundle, error) {
	bundle := i18n.NewBundle(language.English)
	setDefaultUnmarshalFunc(bundle)
	err := loadLangFile(bundle, path)
	if err != nil {
		log.Warnf("[I18N]load lang file error: %v", err)
	}
	return &I18nBundle{
		bundle: bundle,
		funks:  template.FuncMap{},
	}, nil
}

// loadLangFile 递归加载语言文件
func loadLangFile(bundle *i18n.Bundle, path string) error {
	langFiles, err := os.ReadDir(path)
	if err != nil {
		return err
	}
	for _, langFile := range langFiles {
		langFilePath := path + "/" + langFile.Name()
		if !langFile.IsDir() {
			bundle.MustLoadMessageFile(langFilePath)
		} else {
			err = loadLangFile(bundle, langFilePath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func NewLocalized(bundle *I18nBundle, lands ...string) *Localized {
	localize := i18n.NewLocalizer(bundle.bundle, lands...)
	return &Localized{
		lang:     lands[0],
		localize: localize,
		funks:    bundle.funks,
	}
}

// RegisterUnmarshalFunc 注册语言文件解析函数
func (b *I18nBundle) RegisterUnmarshalFunc(format string, unmarshalFunc i18n.UnmarshalFunc) {
	b.bundle.RegisterUnmarshalFunc(format, unmarshalFunc)
}

// RegisterFunks RegisterFunc 注册模板函数
func (b *I18nBundle) RegisterFunks(funks template.FuncMap) {
	b.reMux.Lock()
	defer b.reMux.Unlock()

	for k, v := range funks {
		b.funks[k] = v
	}
}

func (l *Localized) GetLang() string {
	return l.lang
}

// T 本地化带参数的字符串
func (l *Localized) T(messageId string, data interface{}, pluralCounts ...interface{}) string {
	cfg := &i18n.LocalizeConfig{
		MessageID:    messageId,
		TemplateData: data,
		Funcs:        l.funks,
	}

	if len(pluralCounts) > 0 {
		cfg.PluralCount = pluralCounts[0]
	}

	msg, err := l.localize.Localize(cfg)
	if err != nil {
		return ""
	}

	return msg
}

// M 本地化不带参数的字符串
func (l *Localized) M(messageId string) string {
	cfg := &i18n.LocalizeConfig{
		MessageID: messageId,
		Funcs:     l.funks,
	}
	msg, err := l.localize.Localize(cfg)
	if err != nil {
		return ""
	}
	return msg
}

func setDefaultUnmarshalFunc(bundle *i18n.Bundle) {
	bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
	bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
	bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
}
